#!/usr/bin/env node

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

class GitMCPServer {
  private server: Server;

  constructor() {
    this.server = new Server(
      {
        name: 'git-mcp',
        version: '1.0.0',
      },
      {
        capabilities: {
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
  }

  private setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: 'git_status',
            description: 'Get the current git status',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'git_add',
            description: 'Add files to git staging area',
            inputSchema: {
              type: 'object',
              properties: {
                files: {
                  type: 'string',
                  description: 'Files to add (use . for all files)',
                  default: '.',
                },
              },
            },
          },
          {
            name: 'git_commit',
            description: 'Commit staged changes',
            inputSchema: {
              type: 'object',
              properties: {
                message: {
                  type: 'string',
                  description: 'Commit message',
                },
              },
              required: ['message'],
            },
          },
          {
            name: 'git_log',
            description: 'Show git commit history',
            inputSchema: {
              type: 'object',
              properties: {
                count: {
                  type: 'number',
                  description: 'Number of commits to show',
                  default: 10,
                },
              },
            },
          },
          {
            name: 'git_branch',
            description: 'List or create git branches',
            inputSchema: {
              type: 'object',
              properties: {
                action: {
                  type: 'string',
                  enum: ['list', 'create', 'delete'],
                  description: 'Branch action to perform',
                  default: 'list',
                },
                name: {
                  type: 'string',
                  description: 'Branch name (for create/delete actions)',
                },
              },
            },
          },
          {
            name: 'git_push',
            description: 'Push commits to remote repository',
            inputSchema: {
              type: 'object',
              properties: {
                remote: {
                  type: 'string',
                  description: 'Remote name',
                  default: 'origin',
                },
                branch: {
                  type: 'string',
                  description: 'Branch name',
                  default: 'master',
                },
              },
            },
          },
          {
            name: 'git_pull',
            description: 'Pull changes from remote repository',
            inputSchema: {
              type: 'object',
              properties: {
                remote: {
                  type: 'string',
                  description: 'Remote name',
                  default: 'origin',
                },
                branch: {
                  type: 'string',
                  description: 'Branch name',
                  default: 'master',
                },
              },
            },
          },
        ],
      };
    });

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      try {
        switch (name) {
          case 'git_status':
            return await this.gitStatus();
          case 'git_add':
            return await this.gitAdd(args.files || '.');
          case 'git_commit':
            return await this.gitCommit(args.message);
          case 'git_log':
            return await this.gitLog(args.count || 10);
          case 'git_branch':
            return await this.gitBranch(args.action || 'list', args.name);
          case 'git_push':
            return await this.gitPush(args.remote || 'origin', args.branch || 'master');
          case 'git_pull':
            return await this.gitPull(args.remote || 'origin', args.branch || 'master');
          default:
            throw new Error(`Unknown tool: ${name}`);
        }
      } catch (error) {
        return {
          content: [
            {
              type: 'text',
              text: `Error: ${error instanceof Error ? error.message : String(error)}`,
            },
          ],
        };
      }
    });
  }

  private async gitStatus() {
    const { stdout } = await execAsync('git status --porcelain');
    const { stdout: branch } = await execAsync('git branch --show-current');
    
    return {
      content: [
        {
          type: 'text',
          text: `Current branch: ${branch.trim()}\n\nGit Status:\n${stdout || 'Working directory clean'}`,
        },
      ],
    };
  }

  private async gitAdd(files: string) {
    const { stdout, stderr } = await execAsync(`git add ${files}`);
    
    return {
      content: [
        {
          type: 'text',
          text: `Files added to staging area:\n${stdout}${stderr}`,
        },
      ],
    };
  }

  private async gitCommit(message: string) {
    const { stdout, stderr } = await execAsync(`git commit -m "${message}"`);
    
    return {
      content: [
        {
          type: 'text',
          text: `Commit successful:\n${stdout}${stderr}`,
        },
      ],
    };
  }

  private async gitLog(count: number) {
    const { stdout } = await execAsync(`git log --oneline -n ${count}`);
    
    return {
      content: [
        {
          type: 'text',
          text: `Recent commits:\n${stdout}`,
        },
      ],
    };
  }

  private async gitBranch(action: string, name?: string) {
    let command: string;
    
    switch (action) {
      case 'list':
        command = 'git branch -a';
        break;
      case 'create':
        if (!name) throw new Error('Branch name is required for create action');
        command = `git checkout -b ${name}`;
        break;
      case 'delete':
        if (!name) throw new Error('Branch name is required for delete action');
        command = `git branch -d ${name}`;
        break;
      default:
        throw new Error(`Invalid branch action: ${action}`);
    }
    
    const { stdout, stderr } = await execAsync(command);
    
    return {
      content: [
        {
          type: 'text',
          text: `Branch operation result:\n${stdout}${stderr}`,
        },
      ],
    };
  }

  private async gitPush(remote: string, branch: string) {
    const { stdout, stderr } = await execAsync(`git push ${remote} ${branch}`);
    
    return {
      content: [
        {
          type: 'text',
          text: `Push result:\n${stdout}${stderr}`,
        },
      ],
    };
  }

  private async gitPull(remote: string, branch: string) {
    const { stdout, stderr } = await execAsync(`git pull ${remote} ${branch}`);
    
    return {
      content: [
        {
          type: 'text',
          text: `Pull result:\n${stdout}${stderr}`,
        },
      ],
    };
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Git MCP server running on stdio');
  }
}

const server = new GitMCPServer();
server.run().catch(console.error);

